/*
 * @(#)SpringGwtRemoteServiceServlet.java
 *
 * @author <a href="gubarevs@apollo.lv">Denis Gubarev</a>
 */

package com.gshappcore.spring;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import org.apache.log4j.Logger;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@SuppressWarnings("serial")
public class SpringGwtRemoteServiceServlet extends RemoteServiceServlet {

    private static final Logger LOG = Logger.getLogger(SpringGwtRemoteServiceServlet.class);

    @Override
    public void init() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Spring GWT service exporter deployed");
        }
    }

    @Override
    public String processCall(String payload) throws SerializationException {
        Object handler = getBean(getThreadLocalRequest());
        RPCRequest rpcRequest = RPC.decodeRequest(payload, handler.getClass(), this);
        onAfterRequestDeserialized(rpcRequest);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Invoking " + handler.getClass().getName() + "." + rpcRequest.getMethod().getName());
        }
        Method rpcMethod = rpcRequest.getMethod();
        Object[] rpcParameters = rpcRequest.getParameters();
        SerializationPolicy rpcSerializationPolicy = rpcRequest.getSerializationPolicy();

        return RPC.invokeAndEncodeResponse(handler, rpcMethod, rpcParameters, rpcSerializationPolicy);

    }

    protected Object getBean(HttpServletRequest request) {
        String service = getService(request);
        Object bean = getBean(service);
        if (!(bean instanceof RemoteService)) {
            throw new IllegalArgumentException("Spring bean is not a GWT RemoteService: " + service + " (" + bean + ")");
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("Bean for service " + service + " is " + bean);
        }
        return bean;
    }

    protected String getService(HttpServletRequest request) {
        String url = request.getRequestURI();
        String service = url.substring(url.lastIndexOf("/") + 1);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Service for URL " + url + " is " + service);
        }
        return service;
    }

    protected Object getBean(String name) {
        WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        if (applicationContext == null) {
            throw new IllegalStateException("No Spring web application context found");
        }
        if (!applicationContext.containsBean(name)) {
            {
                throw new IllegalArgumentException("Spring bean not found: " + name);
            }
        }
        return applicationContext.getBean(name);
    }
}

